Skip to content

feat: add peerinfo#103

Merged
varex83 merged 30 commits into
mainfrom
bohdan/p2p-peerinfo
Jan 21, 2026
Merged

feat: add peerinfo#103
varex83 merged 30 commits into
mainfrom
bohdan/p2p-peerinfo

Conversation

@varex83
Copy link
Copy Markdown
Collaborator

@varex83 varex83 commented Jan 5, 2026

@varex83 varex83 changed the title feat: add tracing feat: add peerinfo Jan 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 5, 2026

Coverage (base → head): 35.44% → 34.54% ⬇️ 0.9 pp
Only posts when PR coverage is lower than the base branch's latest push (or freshly computed base).

Base automatically changed from bohdan/p2p-relay-server to main January 13, 2026 15:43
Copy link
Copy Markdown
Collaborator

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this PR in general is a matter of trust: I cannot really validate that the implementation is correct and I would not be able to debug if there were issues. Also, the structure is completely different to the Go implementation.

We can go with this approach but I wonder if relying on libp2p_stream wouldn't be easier to debug and result in less code to maintain. I would like to have a second opinion about it (cc. @iamquang95).

Comment thread crates/peerinfo/Cargo.toml Outdated
Comment on lines +14 to +15
futures = "0.3"
futures-timer = "3.0"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependency versions should be listed in the top level Cargo.toml file only.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad, forgot to move

Comment on lines +29 to +41
// Encode message to protobuf bytes
let mut buf = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut buf)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

// Write unsigned varint length prefix
let mut len_buf = unsigned_varint::encode::usize_buffer();
let encoded_len = unsigned_varint::encode::usize(buf.len(), &mut len_buf);
stream.write_all(encoded_len).await?;

// Write protobuf bytes
stream.write_all(&buf).await?;
stream.flush().await
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried it yet but I think following is equivalent:

Suggested change
// Encode message to protobuf bytes
let mut buf = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut buf)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
// Write unsigned varint length prefix
let mut len_buf = unsigned_varint::encode::usize_buffer();
let encoded_len = unsigned_varint::encode::usize(buf.len(), &mut len_buf);
stream.write_all(encoded_len).await?;
// Write protobuf bytes
stream.write_all(&buf).await?;
stream.flush().await
let mut bb = bytes::BytesMut::with_capacity(MAX_MESSAGE_SIZE);
request.encode_length_delimited(&mut bb);
stream.write_all(&bb).await?;

Maybe we could add a test for how the encoded data should look like?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not right way to do, since stream could have mulltiple requests and when you read it to end you will skip them

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for writing so it should not be problematic. As for reading I agree, the stream might contain multiple encoded messages. The prost library contains some helper functions to read the lengths; we might be able to drop the unsigned-varint altogether.

Comment on lines +50 to +68
// Read unsigned varint length prefix
let msg_len = read_usize(&mut *stream).await.map_err(|e| match e {
unsigned_varint::io::ReadError::Io(io_err) => io_err,
other => io::Error::new(io::ErrorKind::InvalidData, other),
})?;

if msg_len > MAX_MESSAGE_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("message too large: {msg_len} bytes (max: {MAX_MESSAGE_SIZE})"),
));
}

// Read exactly `msg_len` protobuf bytes
let mut buf = vec![0u8; msg_len];
stream.read_exact(&mut buf).await?;

// Unmarshal protobuf
M::decode(&buf[..]).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

Suggested change
// Read unsigned varint length prefix
let msg_len = read_usize(&mut *stream).await.map_err(|e| match e {
unsigned_varint::io::ReadError::Io(io_err) => io_err,
other => io::Error::new(io::ErrorKind::InvalidData, other),
})?;
if msg_len > MAX_MESSAGE_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("message too large: {msg_len} bytes (max: {MAX_MESSAGE_SIZE})"),
));
}
// Read exactly `msg_len` protobuf bytes
let mut buf = vec![0u8; msg_len];
stream.read_exact(&mut buf).await?;
// Unmarshal protobuf
M::decode(&buf[..]).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
// Read unsigned varint length prefix
let mut buffer = Vec::with_capacity(MAX_MESSAGE_SIZE);
stream.read_to_end(&mut buffer).await?;
M::decode_length_delimited(buffer.as_slice())

Comment on lines +74 to +77
pub async fn send_peer_info(
mut stream: Stream,
request: &PeerInfo,
) -> io::Result<(Stream, PeerInfo)> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use the following signature?

Suggested change
pub async fn send_peer_info(
mut stream: Stream,
request: &PeerInfo,
) -> io::Result<(Stream, PeerInfo)> {
pub async fn send_peer_info(stream: &mut Stream, request: &PeerInfo) -> io::Result<PeerInfo> {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since it will run as a future which should own a stream, otherwise it could lead to lifetime issues

Comment on lines +10 to +13
const DEFAULT_INTERVAL: Duration = Duration::from_secs(60);

/// Default timeout for peer info requests.
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(20);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From where are we getting these constants?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

impl ConnectionHandler for Handler {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my biggest gripe with the approach: I have no way to review this code or assert that it's correct 😢

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can setup a meeting and I will make a walkthrough the project if you want, but it's the only way to go. Also, it's the easiest module for us to implement among all others, so the sooner we start using native libp2p approach - the better. This approach provides the best code maintainability and resilience

Copy link
Copy Markdown
Collaborator

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some internal discussion we'll move forward with this PR. A few things to keep in mind:

  • We need to eventually test edge case conditions like connections dropping mid message, peers disconecting and spamming peerinfo (potential unlimited vector growth)
  • Add references to the ping which was used as a base for this implementation'
  • Include some tests that validate peerinfo serialization/deserialization (ex. write multiple consecutive peerinfos to a stream and deserialize them)

@varex83 varex83 merged commit 9ad5a5a into main Jan 21, 2026
7 checks passed
@varex83 varex83 deleted the bohdan/p2p-peerinfo branch January 21, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants